package upiot

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"simcard/config"
	"simcard/utils"
	"simcard/utils/limiter"
	"slices"
	"strconv"
	"strings"
)

var (
	UPIot *UpIotCard
)

type UpIotService struct {
}

func (a *UpIotService) init() func() {

	scAgent := config.Conf.SimCardAgent
	UPIot = NewUpIot(scAgent.UPIot.ApiKey, scAgent.UPIot.ApiSecret, scAgent.UPIot.Host)

	return func() {}
}

func init() {
	Register(&UpIotService{})
}

// UpIotCard 量讯物联网卡
type UpIotCard struct {
	apiKey     string
	apiSecret  string
	host       string
	client     http.Client
	limiterMap map[string]*limiter.Limiter
}

func NewUpIot(apiKey, apiSecret string, host string) *UpIotCard {
	return &UpIotCard{
		apiKey:    apiKey,
		apiSecret: apiSecret,
		host:      host,
		limiterMap: map[string]*limiter.Limiter{
			"Balance":                     limiter.NewLimiter(60, 20),
			"ListCards":                   limiter.NewLimiter(60, 20),
			"ListCardsInfo":               limiter.NewLimiter(60, 60),
			"ListRechargeProductsForPool": limiter.NewLimiter(60, 60),
			"MonthDailyUsage":             limiter.NewLimiter(60, 20),
			"RechargeForCardData":         limiter.NewLimiter(60, 60),
		},
		//host:      "http://ec.upiot.net",
	}
}

// 生成GET请求签名
func (c *UpIotCard) genGetSign(kvs map[string]string) (sign string) {
	var keys = make([]string, 0, len(kvs))
	for k, _ := range kvs {
		keys = append(keys, k)
	}
	slices.Sort(keys)
	var builder strings.Builder
	for _, k := range keys {
		builder.WriteString(fmt.Sprintf("%s=%s", k, kvs[k]))
	}
	builder.WriteString(c.apiSecret)
	sign = fmt.Sprintf("%x", md5.Sum([]byte(builder.String())))
	return
}

// 生成POST请求签名
func (c *UpIotCard) genPostSign(jsonParams string) (sign string) {
	nonce := fmt.Sprintf("%s%s", jsonParams, c.apiSecret)
	sign = fmt.Sprintf("%x", md5.Sum([]byte(nonce)))
	return
}

// get 请求
// result为响应值中Data对象的指针，正常请求返回的结果会被解析到result
func (c *UpIotCard) get(requestUrl string, params map[string]string, result any) (*Response, error) {
	// 构造GET参数
	queryParams := url.Values{}
	for k, v := range params {
		queryParams.Set(k, v)
	}
	queryParams.Set("_sign", c.genGetSign(params))
	// 将参数添加到 URL 中
	parseURL, err := url.Parse(requestUrl)
	if err != nil {
		return nil, err
	}
	parseURL.RawQuery = queryParams.Encode()

	req, err := http.NewRequest(http.MethodGet, parseURL.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	// 请求
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	// http状态码异常
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bad http resp status: %d", resp.StatusCode)
	}

	defer resp.Body.Close()
	var body []byte
	if body, err = io.ReadAll(resp.Body); err != nil {
		return nil, err
	}
	var r = &Response{}
	if err = json.Unmarshal(body, &r); err != nil {
		return r, err
	}
	// 业务状态码异常
	if r.Code != OK {
		return r, fmt.Errorf("bad resp code: %v", r)
	}
	return r, c.parse(r, result)
}

// post 请求
func (c *UpIotCard) post(requestURL string, params map[string]any, result any) (*Response, error) {
	// json参数
	jsonBytes, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	jsonStr := string(jsonBytes)

	// 将签名放到url参数中
	queryParams := url.Values{}
	queryParams.Set("_sign", c.genPostSign(jsonStr))
	parsedURL, err := url.Parse(requestURL)
	if err != nil {
		return nil, err
	}
	parsedURL.RawQuery = queryParams.Encode()

	var req *http.Request
	req, err = http.NewRequest(http.MethodPost, parsedURL.String(), strings.NewReader(jsonStr))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	// 请求
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	// http状态码异常
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bad http resp status: %d", resp.StatusCode)
	}

	defer resp.Body.Close()
	var body []byte
	if body, err = io.ReadAll(resp.Body); err != nil {
		return nil, err
	}
	var r = &Response{}
	if err = json.Unmarshal(body, &r); err != nil {
		return r, err
	}
	// 业务状态码异常
	if r.Code != OK {
		return r, fmt.Errorf("bad resp code: %v", r)
	}
	if result == nil {
		// 不获取响应值
		return r, nil
	}
	return r, c.parse(r, result)
}

// 解析resp到target
// target必须为指针
func (c *UpIotCard) parse(resp *Response, target any) error {
	targetValue := reflect.ValueOf(target)
	if targetValue.Kind() != reflect.Ptr {
		return fmt.Errorf("incompatible types: %T and %T", resp.Data, target)
	}
	// Handle different types of resp.Data
	switch t := resp.Data.(type) {
	case map[string]interface{}:
		// If resp.Data is a map, decode it into target
		return mapstructure.Decode(resp.Data, target)
	case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
		// If resp.Data is a basic type, assign it directly to target
		targetValue.Elem().Set(reflect.ValueOf(resp.Data))
		return nil
	default:
		// If Data is a struct, struct pointer, or slice, handle it accordingly
		config := &mapstructure.DecoderConfig{
			TagName: "json",
			Result:  target,
		}
		decoder, err := mapstructure.NewDecoder(config)
		if err != nil {
			return err
		}
		return decoder.Decode(t)
	}
}

// Balance 获取账户余额
func (c *UpIotCard) Balance() (float64, *Response, error) {
	url := fmt.Sprintf("%s/api/v2/%s/balance", c.host, c.apiKey)
	var result string
	if r, err := c.get(url, nil, &result); err != nil {
		return 0, r, err
	}

	f, err := strconv.ParseFloat(result, 64)
	return f, nil, err
}

// ListCards 查询流量卡ID信息
func (c *UpIotCard) ListCards(page int64) (result CardListResult, r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/card_no_list", c.host, c.apiKey)
	params := map[string]string{
		"page": strconv.FormatInt(page, 10),
	}

	r, err = c.get(url, params, &result)
	return
}

// ListCardsInfo 批量查询流量卡信息，如状态、流量使用、套餐、卡号、运营商等等
func (c *UpIotCard) ListCardsInfo(iccids []string) (result []*CardInfo, r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/batch/card/info/", c.host, c.apiKey)
	params := map[string]any{
		"iccids": iccids,
	}
	r, err = c.post(url, params, &result)
	return
}

// ListRechargeProductsForCard 物联网卡充值产品查询
func (c *UpIotCard) ListRechargeProductsForCard(id string) (result ProductsForCardRechargeResult, r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/card/%s/charge/products/", c.host, c.apiKey, id)
	r, err = c.get(url, nil, &result)
	return
}

// ListRechargeProductsForPool 流量池充值产品查询
func (c *UpIotCard) ListRechargeProductsForPool(code string) (result ProductsForPoolRechargeResult, r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/usage_pool/%s/charge/products/", c.host, c.apiKey, code)
	r, err = c.get(url, nil, &result)
	return
}

// RechargeForCardData 流量卡充值
func (c *UpIotCard) RechargeForCardData(id string) (result ChargeForCardDataResult, r *Response, err error) {

	if fLimiter, ok := c.limiterMap[utils.GetFuncName()]; ok {
		fLimiter.Wait()
	}
	var products ProductsForCardRechargeResult
	products, r, err = c.ListRechargeProductsForCard(id)
	if err != nil {
		return
	}
	var product productsForCardRechargeItem
	for _, p := range products.Rows {
		if p.Months == 12 {
			product = p
			break
		}
	}

	url := fmt.Sprintf("%s/api/v2/%s/card/%s/charge/", c.host, c.apiKey, id)
	params := map[string]any{
		"product_id":  product.Id,
		"external_no": "", // TODO
		"effect_type": "next_cycle",
	}
	r, err = c.post(url, params, &result)
	return
}

// RechargeForPollData 流量池充值
func (c *UpIotCard) RechargeForPollData(code string) (result ChargeForPoolDataResult, r *Response, err error) {

	var products ProductsForPoolRechargeResult
	products, r, err = c.ListRechargeProductsForPool(code)
	if err != nil {
		return
	}
	var product productsForPoolRechargeItem
	for _, p := range products.Rows {
		if p.Months == 12 {
			product = p
		}
	}

	url := fmt.Sprintf("%s/api/v2/%s/usage_pool/%s/charge/", c.host, c.apiKey, code)
	params := map[string]any{
		"product_id":  product.Id,
		"external_no": "", // TODO
	}
	r, err = c.post(url, params, &result)
	return
}

// MonthDailyUsage 卡片每日流量使用量
func (c *UpIotCard) MonthDailyUsage(id, month string) (*MonthDailyUsage, *Response, error) {
	url := fmt.Sprintf("%s/api/v2/%s/card/%s/usagelog/%s/delta/", c.host, c.apiKey, id, month)
	var result MonthDailyUsage
	if r, err := c.get(url, nil, &result); err != nil {
		return nil, r, err
	}
	return &result, nil, nil
}

// CloseCard 卡片停机
func (c *UpIotCard) CloseCard(number string) (r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/sor/", c.host, c.apiKey)
	params := map[string]any{
		"number": number,
		"type":   "01",
	}
	r, err = c.post(url, params, nil)
	return
}

// ReopenCard 卡片复机
func (c *UpIotCard) ReopenCard(number string) (r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/sor/", c.host, c.apiKey)
	params := map[string]any{
		"number": number,
		"type":   "00",
	}
	r, err = c.post(url, params, nil)
	return
}

// ListExpiringCards 查询当月（计费月）即将到期的流量卡
func (c *UpIotCard) ListExpiringCards() (result ExpiringCardsResult, r *Response, err error) {
	url := fmt.Sprintf("%s/api/v2/%s/batch/card/expiry_date/", c.host, c.apiKey)
	r, err = c.get(url, nil, &result)
	return
}
